/*
 * ByteStream.cpp
 *
 *  Created on: Jun 25, 2012
 *      Author: burek
 */

#include "ByteStream.h"
#include "../../_config.h"
#include <cstdlib>
#include <cstring>
#include <cstdio>

ByteStream::ByteStream(uint32_t bSize)
{
	void * mem = malloc(bSize);
	if(mem == NULL)
		bufferSize = 0;
	else
		bufferSize = bSize;

	length = 0;
	pos = 0;
	data = (byte *)mem;
}

ByteStream::ByteStream(const char * fileName)
{
	bufferSize = 0;
	length = 0;
	pos = 0;
	data = NULL;
	if(!readPlain(fileName))
	{
		length = 0;
		bufferSize = 0;
	}
}

ByteStream::ByteStream(void * buffer, int32_t size)
{
	if(buffer == NULL)
	{
		length = 0;
		bufferSize = 0;
	}
	else
	{
		bufferSize = size;
		length = size;
	}
	pos = 0;
	data = (byte *)buffer;
}

ByteStream::~ByteStream()
{
	if(data != NULL)
		free(data);
}

void ByteStream::zeroMem()
{
	memset(data, 0, bufferSize);
}

int32_t ByteStream::seekStreamTo(uint32_t Pos)
{
	//if(Pos < 0) Pos = 0;
	if(Pos >= length) Pos = length;
	pos = Pos;
	return pos;
}

int32_t ByteStream::seekToStart()
{
	return seekStreamTo(0);
}

int32_t ByteStream::seekToEnd()
{
	return seekStreamTo(length);
}

bool ByteStream::writeSHORT(int16_t x)
{
	if(pos + sizeof(int16_t) > bufferSize) return false;

#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	memcpy(data + pos, &x, sizeof(int16_t));
	pos += sizeof(int16_t);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeUSHORT(uint16_t x)
{
	if(pos + sizeof(uint16_t) > bufferSize) return false;
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	memcpy(data + pos, &x, sizeof(uint16_t));
	pos += sizeof(uint16_t);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeINT(int32_t x)
{
	if(pos + sizeof(int32_t) > bufferSize) return false;
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	memcpy(data + pos, &x, sizeof(int32_t));
	pos += sizeof(int32_t);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeUINT(uint32_t x)
{
	if(pos + sizeof(uint32_t) > bufferSize) return false;
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	memcpy(data + pos, &x, sizeof(uint32_t));
	pos += sizeof(uint32_t);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeLONG(int64_t x)
{
	if(pos + sizeof(int64_t) > bufferSize) return false;
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	memcpy(data + pos, &x, sizeof(int64_t));
	pos += sizeof(int64_t);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeULONG(uint64_t x)
{
	if(pos + sizeof(uint64_t) > bufferSize) return false;
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	memcpy(data + pos, &x, sizeof(uint64_t));
	pos += sizeof(uint64_t);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeBOOL(bool b)
{
	if(pos + sizeof(bool) > bufferSize) return false;

	memcpy(data + pos, &b, sizeof(bool));
	pos += sizeof(bool);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeFLOAT(float f)
{
	if(pos + sizeof(float) > bufferSize) return false;

	memcpy(data + pos, &f, sizeof(float));
	pos += sizeof(float);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeCHAR(char c)
{
	if(pos + sizeof(char) > bufferSize) return false;

	memcpy(data + pos, &c, sizeof(char));
	pos += sizeof(char);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeUCHAR(unsigned char c)
{
	if(pos + sizeof(unsigned char) > bufferSize) return false;

	memcpy(data + pos, &c, sizeof(unsigned char));
	pos += sizeof(unsigned char);
	if(pos > length)
		length = pos;
	return true;
}

bool ByteStream::writeSTRING(const char * s, uint32_t maxLen)
{
	for(uint32_t i = 0; i < maxLen; ++i)
	{
		if(!writeCHAR(s[i]))
			return false;
		if(s[i] == '\0')
			break;
	}
	return true;
}

bool ByteStream::writeSTRINGn(const char * s, uint32_t maxLen)
{
	for(uint32_t i = 0; i < maxLen; ++i)
	{
		if(s[i] == '\0')
			break;
		if(!writeCHAR(s[i]))
			return false;
	}
	return true;
}

bool ByteStream::writeBUFFER(ByteStream * bs)
{
	if(bs == NULL)
		return false;

	if(pos + bs->length > bufferSize)
		return false;

	memcpy(data + pos, bs->data, bs->length);
	pos += bs->length;
	length = (length > pos) ? length : pos;  // rMax(length, pos);
	return true;
}

bool ByteStream::writeBYTES(byte * b, uint32_t len)
{
	if(b == NULL)
		return false;

	if(pos + len > bufferSize)
		return false;

	memcpy(data + pos, b, len);
	pos += len;
	length = (length > pos) ? length : pos;  // rMax(length, pos);
	return true;
}

bool ByteStream::readSHORT(int16_t & x)
{
	if(pos + sizeof(int16_t) > length) return false;
	memcpy(&x, data + pos, sizeof(int16_t));
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	pos += sizeof(int16_t);
	return true;
}

bool ByteStream::readUSHORT(uint16_t & x)
{
	if(pos + sizeof(uint16_t) > length) return false;
	memcpy(&x, data + pos, sizeof(uint16_t));
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	pos += sizeof(uint16_t);
	return true;
}

bool ByteStream::readINT(int32_t & x)
{
	if(pos + sizeof(int32_t) > length) return false;
	memcpy(&x, data + pos, sizeof(int32_t));
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	pos += sizeof(int32_t);
	return true;
}

bool ByteStream::readUINT(uint32_t & x)
{
	if(pos + sizeof(uint32_t) > length) return false;
	memcpy(&x, data + pos, sizeof(uint32_t));
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	pos += sizeof(uint32_t);
	return true;
}

bool ByteStream::readLONG(int64_t & x)
{
	if(pos + sizeof(int64_t) > length) return false;
	memcpy(&x, data + pos, sizeof(int64_t));
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	pos += sizeof(int64_t);
	return true;
}

bool ByteStream::readULONG(uint64_t & x)
{
	if(pos + sizeof(uint64_t) > length) return false;
	memcpy(&x, data + pos, sizeof(uint64_t));
#ifdef R_BIG_ENDIAN
	R4_endianSwap(x);
#endif
	pos += sizeof(uint64_t);
	return true;
}

bool ByteStream::readBOOL(bool & b)
{
	if(pos + sizeof(bool) > length) return false;
	memcpy(&b, data + pos, sizeof(bool));
	pos += sizeof(bool);
	return true;
}

bool ByteStream::readFLOAT(float & f)
{
	if(pos + sizeof(float) > length) return false;
	memcpy(&f, data + pos, sizeof(float));
	pos += sizeof(float);
	return true;
}

bool ByteStream::readUCHAR(unsigned char & c)
{
	if(pos + sizeof(unsigned char) > length) return false;
	memcpy(&c, data + pos, sizeof(unsigned char));
	pos += sizeof(unsigned char);
	return true;
}

bool ByteStream::readCHAR(char & c)
{
	if(pos + sizeof(char) > length) return false;
	memcpy(&c, data + pos, sizeof(char));
	pos += sizeof(char);
	return true;
}

bool ByteStream::readSTRING(char * s, uint32_t maxLen)
{
	for(uint32_t i = 0; i < maxLen; ++i)
	{
		if(!readCHAR(s[i]))
			return false;
		if(s[i] == '\0')
			break;
	}
	return true;
}

bool ByteStream::readBUFFER(ByteStream * bs, uint32_t len)
{
	if(bs == NULL)
		return false;

	if(len == 0)
		return true;

	if(bs->pos + len > bs->length)
		return false;

	if(bufferSize < pos + len)
		return false;

	memcpy(data + pos, bs->data + bs->pos, len);
	bs->pos += len;
	pos += len;

	return true;
}

bool ByteStream::readBYTES(byte * b, uint32_t len)
{
	if(b == NULL)
		return false;

	if(len == 0)
		return true;

	if(length < pos + len)
		return false;

	memcpy(b, data + pos, len);
	pos += len;
	return true;
}

bool ByteStream::readLINE(char * s, uint32_t maxLen)
{
	for(uint32_t i = 0; i < maxLen; ++i)
	{
		if(!readCHAR(s[i]))
			return false;
		if(s[i] == '\0')
			{s[i] = '\0'; break;}
		if(s[i] == '\n')
			{s[i] = '\0'; break;}
		if(s[i] == '\r')
			{s[i] = '\0'; break;}
	}

	while(pos < length)
	{
		if(data[pos] == '\n' || data[pos] == '\r')
			pos++;
		else
			break;
	}

	return true;
}

bool ByteStream::writePlain(const char * name)
{
	if(length < 1) return false;

	FILE * f = fopen(name, "wb");
	if(f == NULL)
		return false;

	if(fwrite(data, 1, length, f) < length)
	{
		fclose(f);
		return false;
	}

	fclose(f);
	return true;
}

bool ByteStream::readPlain(const char * name)
{
	pos = 0;

	FILE * f = fopen(name, "rb");
	if(f == NULL)
	{
		return false;
	}

	fseek(f, 0, SEEK_END);
	unsigned int bsize = ftell(f);
	fseek(f, 0, SEEK_SET);

	if(length < bsize)
	{
		unsigned char * newPtr = (unsigned char *)realloc(data, bsize + 1);
		if(newPtr == NULL)
		{
			fclose(f);
			return false;
		}
		data = newPtr;
	}

	length = fread(data, 1, bsize, f);

	if(length < bsize)
	{
		fclose(f);
		return false;
	}

	data[bsize] = 0;

	fclose(f);
	return true;
}
